package com.study.demo.hutool;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.study.demo.hutool.model.Province;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class TestService {

    public static void main(String[] args) {
        /**
         * 熟悉JS的看到这两个方法一定不陌生，甚至是“旧交”，其实Hutool中的Console对象借鉴的就是JS中的语法糖。
         * System.out.println()/System.err.println() 还是有所不同的，最重要的是它支持Slf4j的字符串模板语法，会自动将对象（包括数组）转为字符串形式
         *
         * 这里的{}作为模板占位符，能够将逗号右边的变量值依次传入，从而以字符串的形式打印输出。
         */
        Console.log("this is array:{}", (Object) new String[]{"Java","JavaScript","Python"});
        Console.error("I'm a red error message.");

        /**
         * Convert 不同类型之间转换类，用于简化类型转换，同时对转换失败的异常捕获有着很好的封装，能够帮你减轻业务代码的臃肿，提升代码的优雅性。
         */
        //转换为字符串
        int number = 10;
        Console.log("转换成字符串后:{}", Convert.toStr(number));

        int[] arr = {1,2,3,4,5};
        Console.log(Convert.toStr(arr,"Convert to string fail! This is default value."));

        //转换为指定类型的数组
        String[] letters = {"12","122","1223","1211"};
        Console.log(Convert.toShortArray(letters));
        Console.log(Convert.toStrArray(arr));

        float[] floatArr = {1.2F,1.3F,2.0F,3.14F};
        Integer[] integerArr = Convert.toIntArray(floatArr);
        Console.log(integerArr); // [1,1,2,3]

        //转换为日期对象
        String strDate1 = "2022-02-01 11:59:59";
        //toDate
        Console.log("The string of data convert to Date:{}",Convert.toDate(strDate1));
        // toLocalDateTime
        Console.log("The string of data convert to LocalDateTime:{}",Convert.toLocalDateTime(strDate1));

        //转换集合
        String[] langs = {"Java","JavaScript","Python","C++","Golang","TypeScript","Kotlin"};

        //通过Convert.convert(Class<T>,Object) 方法可以将任意类型转换为指定类型
        ArrayList arrayList = Convert.convert(ArrayList.class,langs);
        //也可以这样转集合
        List<String> langList = Convert.toList(String.class,langs);
        Console.log("String array is converted to ArrayList:{}",arrayList);
        Console.log("String array is converted to List:{}",langList);

        //转成指定类型的集合
        Object[] objArr = {"a","你","好","",1};
        //TypeReference 对象可以对嵌套泛型进行类型转换
        List<String> strList = Convert.convert(new TypeReference<List<String>>() {},objArr);

        Console.log("使用TypeReference对象可以对嵌套泛型进行类型转换:{}", strList);

        /**
         * 字符串转为16进制(Hex)和 Unicode串
         */
        //字符串转为16进制(Hex)
        String desc = "大家好，我的名称叫HELLO";
        //因为字符串牵扯到编码问题，因为必须传入编码对象
        String hexStr = Convert.toHex(desc, CharsetUtil.CHARSET_UTF_8);
        Console.log("将字符串转为 Hex字符串:{}",hexStr);

        //将字符串转换为 Unicode
        String unicodeStr = Convert.strToUnicode(desc);
        Console.log("字符串转为 Unicode串:{}",unicodeStr);

        /**
         * 字符编码方式转换（编码与解码）
         */
        String a = "我不是乱码";
        //使用UT8字符编码解码为ISO_8859_1 编码方式的字符串
        String mess = Convert.convertCharset(a,CharsetUtil.UTF_8,CharsetUtil.ISO_8859_1);
        Console.log("ISO_8859_1 编码方式的中文乱码:{}",mess);


        /**
         *
         * 中文大/小写数字、金额相关转换
         */
        //金额转中文表达（最多保留到小数点后两位）
        double money = 18.88D;
        String chineseWord = Convert.digitToChinese(money);
        Console.log("金额转中文表达：{}",chineseWord);

        //金额转英文表达（最多保留到小数点后两位）
        String englishWord = Convert.numberToWord(money);
        Console.log("金额转英文表达：{}",englishWord);

        double amount = 102389942.32D;
        //数字转中文大写
        String traditionalChinese = Convert.numberToChinese(amount,true);
        // 数字转中文小写
        String nonTraditionalChinese = Convert.numberToChinese(amount,false);
        Console.log("数字转中文大写，传统：{};非传统：{}",traditionalChinese,nonTraditionalChinese);

        //中文大写 壹仟叁佰转成纯数字
        Console.log("中文大写壹仟叁佰转成纯数字：{}",Convert.chineseToNumber("壹仟叁佰"));

        //数字简化 1000 => 1k; 10000 => 1w 最多保留到小数点后两位
        String simple = Convert.numberToSimple(amount);
        Console.log("{} 简化为:{}",amount,simple);


        //毫秒数
        long ms = 100000000L;
        long msToDays = Convert.convertTime(ms, TimeUnit.MILLISECONDS,TimeUnit.DAYS);
        Console.log("{} 毫秒约等于{} 天",ms,msToDays);

        //原始类型和包装类型之间的转换
        //Integer包装类
        Class<Integer> wrapClass = Integer.class;

        //Integer 去除包装类 => int
        Class<?> unWraped = Convert.unWrap(wrapClass);
        Console.log("Integer 包装类去包装化:{}",unWraped);

        //原始类
        Class<?> primitiveClass = long.class;

        //将原始类型转换成包装类
        Class<?> wraped = Convert.wrap(primitiveClass); // class java.lang.lang
        Console.log("long 原始类包装化：{}",wraped);


        /**
         * DateUtil DateTime 日期时间工具类
         * 使用了一个DateTime类来替代，继承自Date，重写了toString() 方法，直接返回 yyyy-MM-dd HH:mm:ss 形式的字符串，方便在输出时调用
         */
        //计时器，计算被其包裹的代码片段执行时间
        final TimeInterval timeInterval= DateUtil.timer();


        //根据birthday计算现在的年龄
        String dateStr = "2000年05月26日";

        Date birthday = Convert.toDate(dateStr);
        //可以通过DatePattern指定dateStr 日期时间格式进行解析，如果没有第二个参数，则会自动寻找合适的
        //DateTime dateTime = new DateTime(dateStr,DatePattern.CHINESE_DATE_PATTERN);
        int age = DateUtil.ageOfNow(birthday);
        Console.log("{} 出生的小伙子，如果已经{}岁了！",dateStr,age);

        //获取当前时间，格式 yyyy-MM-dd HH:mm:ss => DateTime类型
        Date currentDate = DateUtil.date();
        //等价于DateTime currentTime = new Datetime();
        Console.log("DateUtil.date() 得到当前时间(yyyy-MM-dd HH:mm:ss):",currentDate);

        //将特殊的日期字符串转换为DateTime对象
        String strDate2 = "2022年02月22日 11时59分59秒";
        //根据格式化模板将字符串日期转换为Date对象
        Console.log("Use DateUtil.parse() method convert to Date:{}",DateUtil.parse(strDate2,"yyyy年MM月dd日 HH时mm分ss秒"));

        // 当前日期字符串，格式： yyyy-MM-dd
        String today = DateUtil.today();
        Console.log("当前日期字符串，格式：yyyy-MM-dd:{}",today);

        //运行时间转换为秒为单位
        long sec = timeInterval.intervalSecond();

        //返回花费时间，并重置开始时间
        long spendTimeAndRestart = timeInterval.intervalRestart();

        Console.log("测试运行时间，转换为秒:{}",sec);

        Console.log("测试运行时间（无单位不美化）：",spendTimeAndRestart);


        //计算两个时间间隔
        //当前时间
        DateTime currentDateTime = new DateTime();

        String dateTimeStrStart = "1949/10/01";

        Date startTime = Convert.toDate(dateTimeStrStart);
        long betweenDays = DateUtil.between(startTime,new DateTime(), DateUnit.DAY);

        Console.log("距离新中国成立，已经过了{}天",betweenDays);

        //下班时间 (当天17:00:00)
        Date getOffTime = DateUtil.offsetHour(DateUtil.beginOfDay(currentDateTime),17);
        Console.log("下班时间是：{}",getOffTime);

        long distance = DateUtil.between(getOffTime,currentDateTime,DateUnit.SECOND,true);

        //间隔时间设置为秒
        Console.log("距离下班还剩{}秒",distance);
        //格式化间隔时间
//        Console.log("距离下班还剩{}精确到秒",DateUtil.formatBetween(getOffTime,currentDateTime,));


        /**
         * 针对Object通用的的工具类的方法，不区分是String还是Object、Array还是Collection
         * 判断 null/blank string
         */

        final String blankStr = " ";
        final String nullStr = null;
        final String dateStr2 = null;

        // isEmpty/isNull 只有为null 才返回true
        System.out.println(ObjectUtil.isEmpty(blankStr));//false
        System.out.println(ObjectUtil.isEmpty(nullStr));// true
        System.out.println(ObjectUtil.isNull(nullStr));// true
        System.out.println(ObjectUtil.isNull(blankStr)); // false

        //null/空格字符串都是blank
        System.out.println(ObjectUtil.defaultIfBlank(blankStr,"目标字符串为空，我是默认"));
        System.out.println(ObjectUtil.defaultIfBlank(nullStr,"目标字符串为空，我是默认"));

        //此处判断如果dateStr为null，则调用instant.now() 不为null则执行
//        System.out.println(ObjectUtil.defaultIfNull(dateStr2,()->DateUtil.parse(dateStr2, DatePattern.NORM_DATE_FORMAT)));

        System.out.println(ObjectUtil.defaultIfNull(nullStr,"目标字符串为null，我是默认"));
        System.out.println(ObjectUtil.defaultIfNull(blankStr,"目标字符串为null，我是默认"));

        //ObjectUtil.isNull()方法不能判断对象中字段为空的情况，如果需要检查Bean对象中字段是否全空，需使用BeanUtil.isEmpty()方法进行判定

        //判断两个对象是否相等，等价于Objects.equals()方法
        ObjectUtil.equal(nullStr,dateStr2);//true
        ObjectUtil.equal("HELLO","HELLO");// true
        //等价于
        Objects.equals(nullStr,dateStr2);//true


        //计算对象长度
        int[] array = new int[]{1,2,3,4,5};
        //计算数组长度
        int length = ObjectUtil.length(array);//5

        Map<String,String> map = new HashMap<>();
        map.put("a","a1");
        map.put("b","b1");
        map.put("c","c1");

        //计算map大小
        int mapLen = ObjectUtil.length(map);// 3

        /**
         * StrUtil 处理字符串工具
         */
        //去除前缀

        //去除后缀
        String fileName = StrUtil.removeSuffix("saaa.png",".png");
        Console.log("去除文件扩展名后:{}",fileName);

        //字符串常量，包括点、空字符串、换行符，还有一些HTML中的转义字符
        System.out.println(StrUtil.AT.concat("HELLO").concat(StrUtil.DOT).concat(StrUtil.LF));

        //使用字符串模板替代字符串拼接
        String template = "{}爱{},我爱你，蜜雪冰城{}";
        String str = StrUtil.format(template,"你","我","甜蜜蜜");

        /**
         * CollUtil | CollStreamUtil集合处理工具类
         * 这两个类主要
         */
//        CollStreamUtil.toList();

//        CollUtil.countMap();
        String[] col= new String[]{"a","b","c","d","e"};
        List<String> colList = CollUtil.newArrayList(col);
        String str11 = CollUtil.join(colList, "#"); //str -> a#b#c#d#e


        /**
         * ExcelUtil
         */
        //写入Excel中的数据源
        List<Province> provinces = new ArrayList<>();
        //写出文件路径
        String inputPath = "";

        //通过工具类创建一个Writer写入对象
        ExcelWriter excelWriter = ExcelUtil.getWriter(inputPath,"省份信息表");

        //自定义表头（方法一）
        excelWriter.addHeaderAlias("pid","省会ID");
        excelWriter.addHeaderAlias("province","省份");
        excelWriter.addHeaderAlias("abbr","简称");
        excelWriter.addHeaderAlias("area","面积");
        excelWriter.addHeaderAlias("population","人口数量");
        excelWriter.addHeaderAlias("attraction","著名景点");
        excelWriter.addHeaderAlias("capital","省份");

        // 自定义表头名（方法二）
        Map<String,String> headerAlias = new LinkedHashMap<>(7);

        for (Field field : ReflectUtil.getFields(Province.class)){
            String name = field.getName();

            String alias;
            switch (name){
                case "pid":
                    alias = "省会ID";
                    break;
                case "province":
                    alias = "省份";
                    break;
                case "abbr":
                    alias = "简称";
                    break;
                case "area":
                    alias = "面积";
                    break;
                case "population":
                    alias = "人口数量";
                    break;
                case "attraction":
                    alias = "著名景点";
                    break;
                case "capital":
                    alias = "省份";
                    break;
                default:
                    alias = null;
            }

            if(ObjectUtil.isNotEmpty(alias)){
                headerAlias.put(field.getName(),alias);
            }
        }

        //使用无序的HashMap入参表头也跟着无序，需使用LinkedHashMap，保证有序
        excelWriter.setHeaderAlias(headerAlias);

        //写出数据源到Excel当中，使用默认样式，强制输出标准
        excelWriter.write(provinces,true);

        //关闭writer,释放内存
        excelWriter.close();

    }
}
